﻿//----------------------------------------------------------------
// Desc: 
// Edit: ShowLong, http://blog.csdn.net/showlong
//
// Date Created: 2012-10-26
//
// Update Date : 
//
// Copyright (c) Fuel4D (http://www.fuel4d.com), All rights reserved.
//
//    //新建
//    IFPackage* pk = FNewPackage(FNEW_INFO);
//    pk->AddFile(_FT("..\\sample\\model\\01_fmd_mat.max")); // 添加文件时必须使用能够找到及打开的路径
//    pk->AddFile(_FT("..\\sample\\model\\03_tree.max"));
//    pk->Save(_FT("..\\sample\\xxx.fpk"));    // 保存压缩包
//    FDel(pk);
//    
//    // 加载
//    IFPackage* pk2 = FNewPackage(FNEW_INFO);
//    FV_BYTE max01, max02;
//    if (pk2->Load(_FT("..\\sample\\xxx.fpk")))   // 打开包
//    {
//        pk2->GetFile(_FT("..\\sample\\model\\01_fmd_mat.max"), max01);   // 获取文件
//        pk2->GetFile(_FT("02_bone_anim.max"), max02);    // 从包中获取文件的时候可以只输入文件名和扩展名
//        pk2->Extract(_FT("03_tree.max"), _FT("..\\sample\\03_tree.max")); // 提取文件
//    }
//    FDel(pk2);
//    
//    // 追加与删除
//    IFPackage* pk3 = FNewPackage(FNEW_INFO);
//    if (pk3->Load(_FT("..\\sample\\xxx.fpk")))
//    {
//        pk3->AddFile(_FT("..\\sample\\model\\02_bone_anim.max"));
//        pk3->AddFile(_FT("..\\sample\\model\\04_npc_skin.max"));
//        pk3->AddFile(_FT("..\\sample\\model\\04_npc_skin.max"));
//        pk3->AddFile(_FT("..\\sample\\model\\05_effect.max"));
//        pk3->DelFile(_FT("01_fmd_mat.max")); // 删除文件
//        pk3->Save(0);   // 填0表示：保存当前的修改到包文件
//        pk3->Save(_FT("..\\sample\\xxx2.fpk")); // 另存为，且不会修原始文件名
//        pk3->DelFile(_FT("05_effect.max"));
//        pk3->Save(_FT("..\\sample\\xxx3.fpk")); // 另存为，且不会修原始文件名
//    }
//    FDel(pk3);
//
//    FV_FPK_FILE vadd, vdel, vmdf;
//    {   // 生成差异包
//        IFPackage* pk1 = FNewPackage(FNEW_INFO);
//        IFPackage* pk2 = FNewPackage(FNEW_INFO);
//        pk1->Load(_FT("..\\sample\\xx1.fpk")); // 这是上个版本生成的整个包文件
//        pk2->Load(_FT("..\\sample\\xx2.fpk")); // 这是本次版本生成的整个包文件
//        pk1->Diff(pk2, vadd, vdel, vmdf, _FT("..\\sample\\xx_diff.fpk")); // 生成增加、修改、删除信息，及差异包
//        pk1->Save(_FT("..\\sample\\xx_merge.fpk"));
//        FDel(pk2);
//        FDel(pk1);
//    }
//    {   // 合并差异包
//        IFPackage* pk1 = FNewPackage(FNEW_INFO);
//        IFPackage* pk2 = FNewPackage(FNEW_INFO);
//        pk1->Load(_FT("..\\sample\\xx_merge.fpk"));
//        pk2->Load(_FT("..\\sample\\xx_diff.fpk"));
//        FV_HASH64 del_lst;
//        for (size_t i = 0; i < vdel.size(); ++i)
//            del_lst.push_back(vdel[i].hash);
//        pk1->Merge(pk2, del_lst); // 合并差异包文件，并删除生成差异包时产生的删除列表
//        FDel(pk2);
//        FDel(pk1);
//    }
//    {   // 添加目录
//        IFPackage* pk = FNewPackage(FNEW_INFO);
//        FVString vdir;
//        vdir.push_back(_FT("ui"));
//        vdir.push_back(_FT("world"));
//        FVString vext;
//        vext.push_back(_FT(".db"));
//        vext.push_back(_FT(".log"));
//        vext.push_back(_FT(".bak"));
//        vext.push_back(_FT(".max"));
//        pk->AddDir(_FT("..\\sample\\"), true, &vdir, &vext, eIMM_IGNORE);
//        pk->AddDir(_FT("..\\sample\\twainbump\\"), true, &vdir, &vext, eIMM_IGNORE);
//        pk->Save(_FT("..\\xxx.fpk"));
//        FDel(pk);
//    }
//----------------------------------------------------------------
#pragma once


#pragma pack(push, 1)
struct FPK_FILE
{
    const ACHAR* file;
    FIDHASH64 hash;

    FPK_FILE()
    {}
    FPK_FILE(const ACHAR* f, FIDHASH64 h)
        :file(f)
        ,hash(h)
    {}
    FINLINE operator FIDHASH64 ()
    {
        return hash;
    }
    FINLINE bool operator > (const FPK_FILE& r)
    {
        return hash > r.hash;
    }
    FINLINE bool operator < (const FPK_FILE& r)
    {
        return hash < r.hash;
    }
    FINLINE bool operator == (const FPK_FILE& r)
    {
        return hash == r.hash;
    }
};
#pragma pack(pop)


typedef FVector< FPK_FILE > FV_FPK_FILE;


class IFPackage : public IFRefObject
{
protected:
    explicit IFPackage()
    {}

public:
    // 保存当前数据包到文件（注意添加和删除操作必须调用保存才能生效）
    // .file 要保存到的目标文件，与原始加载文件不同时表示另存合并结果，填0时表示保存之前的修改
    FINLINE bool Save (const NCHAR* file);
    virtual bool SaveW(const WCHAR* file) = 0;
    virtual bool SaveA(const ACHAR* file) = 0;
    // 打开数据包文件
    FINLINE bool Load (const NCHAR* file);
    virtual bool LoadW(const WCHAR* file) = 0;
    virtual bool LoadA(const ACHAR* file) = 0;
    // 添加文件到数据包（不打开文件包的情况下也可以调用，非长文件目录模式下，文件名重复的不被添加）
    FINLINE bool AddFile (const NCHAR* file);
    virtual bool AddFileW(const WCHAR* file) = 0;
    virtual bool AddFileA(const ACHAR* file) = 0;
    // 添加目录到数据包（非长文件目录模式下，文件名重复的不被添加）
    // .dir 导入目录
    // .sub_dir 含子目录中的文件也一起导入
    // .vdir 目录名列表（用法由vec_mark决定），只与该目录名完全匹配，不是全路径，无斜杠
    // .vext 文件扩展名列表（用法由vec_mark决定），要带上点，如“.png”
    // .vec_mark 此值决定前面三个列表的作用（详见EF_IGNORE_MATCH_MODE）
    // .return 返回成功添加的文件数
    FINLINE size_t AddDir (const NCHAR* dir, bool sub_dir, FVString * vdir, FVString * vext, DWORD vec_mark = eIMM_IGNORE);
    virtual size_t AddDirW(const WCHAR* dir, bool sub_dir, FVStringW* vdir, FVStringW* vext, DWORD vec_mark = eIMM_IGNORE) = 0;
    virtual size_t AddDirA(const ACHAR* dir, bool sub_dir, FVStringA* vdir, FVStringA* vext, DWORD vec_mark = eIMM_IGNORE) = 0;
    // 删除数据包文件
    // .file 需要从包中删除的文件，可以只输入文件名和扩展名
    // .return 返回操作结果
    FINLINE bool DelFile (const NCHAR* file);
    virtual bool DelFileW(const WCHAR* file) = 0;
    virtual bool DelFileA(const ACHAR* file) = 0;
    // 获取数据包文件
    // .file 需要读取的文件数据，可以只输入文件名和扩展名
    // .out 输出文件名内容
    // .return 返回操作结果
    FINLINE bool GetFile (const NCHAR* file, FV_BYTE& out);
    virtual bool GetFileW(const WCHAR* file, FV_BYTE& out) = 0;
    virtual bool GetFileA(const ACHAR* file, FV_BYTE& out) = 0;
    // 提取数据包文件到指定位置
    FINLINE bool Extract (const NCHAR* file, const NCHAR* dst_file);
    virtual bool ExtractW(const WCHAR* file, const WCHAR* dst_file) = 0;
    virtual bool ExtractA(const ACHAR* file, const ACHAR* dst_file) = 0;
    // 与另一个包比较，获得当前包到新包之间新增、删除、修改的文件列表
    // .right 另一个包
    // .vadd,vdel,vmdf 输出添加、删除、修改的文件列表（这些字符串串指针在释放包之前有效）
    // .diff_file 是否将新增和修改的数据生成新的包，用于更新程序，填0则表示只分析不打包
    FINLINE bool Diff (IFPackage* right, FV_FPK_FILE& vadd, FV_FPK_FILE& vdel, FV_FPK_FILE& vmdf, const NCHAR* diff_file = 0);
    virtual bool DiffW(IFPackage* right, FV_FPK_FILE& vadd, FV_FPK_FILE& vdel, FV_FPK_FILE& vmdf, const WCHAR* diff_file = 0) = 0;
    virtual bool DiffA(IFPackage* right, FV_FPK_FILE& vadd, FV_FPK_FILE& vdel, FV_FPK_FILE& vmdf, const ACHAR* diff_file = 0) = 0;
    // 与另一个包合并，已经存在文件以新包文件为准，旧文件不变
    // .right 新包（新加和需修改的文件包）
    // .vdel 需要删除旧包的文件
    virtual bool Merge(IFPackage* right, FV_HASH64& vdel) = 0;
    // 获取当前打开的包内文件列表
    // .vfile 输出文件列表（这些字符串串指针在释放包之前有效）
    // .only_name 是否只要文件名和扩展名
    virtual void GetFileList(FV_FPK_FILE& vfile, bool only_name) = 0;
    // 消毁数据包
    virtual void Release() = 0;
};

#include "Core/IFDoc_Package_inl.h"

// 创建包体文件
// .long_dir 是否长文件路径模式，影响添加与读取文件（均忽略大小写）
//       rue,模式下添加和读取文件时需要使用全体相对路径
//       false,模式下，全包文件名采用唯一模式，任意目录位置的文件如出现重名，后都被忽略
// .pwd 密码，如果没有密码填0
extern FAPI IFPackage* FNewPackage(FNEW_FUNC_API, bool long_dir, const ACHAR* pwd);